﻿using System;
using System.Collections.Generic;
using System.Linq;
using Ewk.MachineLearning.Genetic.Programming.Program.Functions;

namespace Ewk.MachineLearning.Genetic.Programming
{
    public class FunctionFactory
    {
        public Function CreateNumericToNumericFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, NumericTypes, NumericTypes);
        }

        public Function CreateNumericToEnumerableOfNumericFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, NumericTypes, EnumerableOfNumericTypes);
        }

        public Function CreateEnumerableOfNumericToNumericFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, EnumerableOfNumericTypes, NumericTypes);
        }

        public Function CreateEnumerableOfNumericToEnumerableOfNumericFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, EnumerableOfNumericTypes, EnumerableOfNumericTypes);
        }

        public Function CreateEnumerableOfNumericToStringFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, EnumerableOfNumericTypes, StringTypes);
        }

        public Function CreateNumericToEnumerableOfStringFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, NumericTypes, EnumerableOfStringTypes);
        }

        public Function CreateNumericToStringFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, NumericTypes, StringTypes);
        }

        public Function CreateStringToStringFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, StringTypes, StringTypes);
        }

        public Function CreateEnumerableOfStringToStringFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, EnumerableOfStringTypes, StringTypes);
        }

        public Function CreateStringToEnumerableOfStringFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, StringTypes, EnumerableOfStringTypes);
        }

        public Function CreateEnumerableOfStringToEnumerableOfStringFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction)
        {
            return CreateFunction(value, arity, readableFunction, EnumerableOfStringTypes, EnumerableOfStringTypes);
        }

        public Function CreateFunction(Func<IEnumerable<dynamic>, dynamic> value, int arity, string readableFunction, IEnumerable<Type> inputTypes, IEnumerable<Type> outputTypes)
        {
            inputTypes = inputTypes.ToList();

            var ioDefinitions = new List<IODefinition>();
            for (var i = 0; i < arity; i++)
            {
                ioDefinitions.Add(new IODefinition(inputTypes));
            }

            return new Function(value,
                                ioDefinitions,
                                new IODefinition(outputTypes),
                                readableFunction);
        }

        public IEnumerable<Type> NumericTypes
        {
            get
            {
                return new List<Type>
                           {
                               typeof(decimal),
                               typeof(double),
                               typeof(float),
                               typeof(int),
                               typeof(long),
                               typeof(sbyte),
                               typeof(short),
                           };
            }
        }

        public IEnumerable<Type> StringTypes
        {
            get
            {
                return new List<Type>
                           {
                               typeof(string),
                           };
            }
        }

        public IEnumerable<Type> EnumerableOfNumericTypes
        {
            get
            {
                return new List<Type>
                           {
                               typeof(IEnumerable<decimal>),
                               typeof(IEnumerable<double>),
                               typeof(IEnumerable<float>),
                               typeof(IEnumerable<int>),
                               typeof(IEnumerable<long>),
                               typeof(IEnumerable<sbyte>),
                               typeof(IEnumerable<short>),
                           };
            }
        }

        public IEnumerable<Type> EnumerableOfStringTypes
        {
            get
            {
                return new List<Type>
                           {
                               typeof(IEnumerable<string>),
                           };
            }
        }
    }
}